import asyncio

from mini_web import request_analysis
from mini_web import response, response_config


# 创建http协议处理对象
class HttpProtocol(asyncio.Protocol):
    """
    asyncio 异步io协议 方法执行顺序：
    connection_made -> data_received -> eof_received -> connection_lost
    """
    def __init__(self, loop: asyncio.get_event_loop(), server):
        self.loop = loop
        self.transport = None
        self.server = server

    def connection_made(self, transport):
        """
        连接建立时会调用的方法，
        :param transport transport可以理解为是一个 上下文，transport 对象中包含了客户端的链接信息
        """

        self.transport = transport       # 将上下文对象保存到对象属性中

    def data_received(self, data):
        """
        当创建完连接,执行完connection_made初始化的时候，会调用这个方法，将请求的数据传递到这里，
        在这里可以处理解析请求协议、路由、数据，
        :param data 就是请求的数据（二进制）
        """

        # 处理解析http 请求， 将请求信息封装为一个 request 请求对象
        request = request_analysis.analysis(data.decode())

        # 将请求处理放到事件循环中
        self.loop.create_task(self.call_route_func(request))

    def eof_received(self):
        """
        有些请求是通过eof描述符来代表结束的， 遇到这种协议的请求时，会调用这个方法
        :return None:
        """

        if self.transport.can_write_eof():
            self.transport.write_eof()

    def connection_lost(self, exc):
        """
        请求结束收尾工作，不管有没有异常都会执行到
        :param exc: 参数 exc 是异常信息, 如果没有异常，exc 参数就是None
        :return:
        """
        pass


    async def call_route_func(self, request):
        """
        异步调用路由方法
        :param request: request 是请求对象，请求信息（url,http 请求方法，请求数据...）都封装在请求对象中
        :return:
        """

        # 校验是否是http GET 或 POST 请求
        methods = self.server.func_map.get(request.method, None)
        if not methods:
            self.error_dispose(101)
            return "方法错误"

        # 判断该路由方法是否存在
        func = methods.get(request.url, None)
        if not func:
            self.error_dispose(404)
            return "路由没找到 "

        # 调用路由方法
        callback = await func(request)

        data = callback.response()
        # 返回响应
        self.transport.write(data.encode())
        self.transport.close()

    def error_dispose(self, code):
        """
        :param code 错误响应码，在request_config 中配置有一些常见的错误
        """
        error_callback = response.HtmlResponse(response_config.response_msg_map[code])
        data = error_callback.response(code=code)
        self.transport.write(data.encode())
        self.transport.close()
